package encoder

import (
	"github.com/makiuchi-d/gozxing"
)

var (
	// Lookup table which factors to use for which number of error correction codewords.
	// See FACTORS.
	factorSets = []int{
		5, 7, 10, 11, 12, 14, 18, 20, 24, 28, 36, 42, 48, 56, 62, 68,
	}

	// Precomputed polynomial factors for ECC 200.
	factors = [][]int{
		{228, 48, 15, 111, 62},
		{23, 68, 144, 134, 240, 92, 254},
		{28, 24, 185, 166, 223, 248, 116, 255, 110, 61},
		{175, 138, 205, 12, 194, 168, 39, 245, 60, 97, 120},
		{41, 153, 158, 91, 61, 42, 142, 213, 97, 178, 100, 242},
		{156, 97, 192, 252, 95, 9, 157, 119, 138, 45, 18, 186, 83, 185},
		{83, 195, 100, 39, 188, 75, 66, 61, 241, 213, 109, 129, 94, 254, 225, 48, 90, 188},
		{15, 195, 244, 9, 233, 71, 168, 2, 188, 160, 153, 145, 253, 79, 108, 82, 27, 174, 186, 172},
		{52, 190, 88, 205, 109, 39, 176, 21, 155, 197, 251, 223, 155, 21, 5, 172,
			254, 124, 12, 181, 184, 96, 50, 193},
		{211, 231, 43, 97, 71, 96, 103, 174, 37, 151, 170, 53, 75, 34, 249, 121,
			17, 138, 110, 213, 141, 136, 120, 151, 233, 168, 93, 255},
		{245, 127, 242, 218, 130, 250, 162, 181, 102, 120, 84, 179, 220, 251, 80, 182,
			229, 18, 2, 4, 68, 33, 101, 137, 95, 119, 115, 44, 175, 184, 59, 25,
			225, 98, 81, 112},
		{77, 193, 137, 31, 19, 38, 22, 153, 247, 105, 122, 2, 245, 133, 242, 8,
			175, 95, 100, 9, 167, 105, 214, 111, 57, 121, 21, 1, 253, 57, 54, 101,
			248, 202, 69, 50, 150, 177, 226, 5, 9, 5},
		{245, 132, 172, 223, 96, 32, 117, 22, 238, 133, 238, 231, 205, 188, 237, 87,
			191, 106, 16, 147, 118, 23, 37, 90, 170, 205, 131, 88, 120, 100, 66, 138,
			186, 240, 82, 44, 176, 87, 187, 147, 160, 175, 69, 213, 92, 253, 225, 19},
		{175, 9, 223, 238, 12, 17, 220, 208, 100, 29, 175, 170, 230, 192, 215, 235,
			150, 159, 36, 223, 38, 200, 132, 54, 228, 146, 218, 234, 117, 203, 29, 232,
			144, 238, 22, 150, 201, 117, 62, 207, 164, 13, 137, 245, 127, 67, 247, 28,
			155, 43, 203, 107, 233, 53, 143, 46},
		{242, 93, 169, 50, 144, 210, 39, 118, 202, 188, 201, 189, 143, 108, 196, 37,
			185, 112, 134, 230, 245, 63, 197, 190, 250, 106, 185, 221, 175, 64, 114, 71,
			161, 44, 147, 6, 27, 218, 51, 63, 87, 10, 40, 130, 188, 17, 163, 31,
			176, 170, 4, 107, 232, 7, 94, 166, 224, 124, 86, 47, 11, 204},
		{220, 228, 173, 89, 251, 149, 159, 56, 89, 33, 147, 244, 154, 36, 73, 127,
			213, 136, 248, 180, 234, 197, 158, 177, 68, 122, 93, 213, 15, 160, 227, 236,
			66, 139, 153, 185, 202, 167, 179, 25, 220, 232, 96, 210, 231, 136, 223, 239,
			181, 241, 59, 52, 172, 25, 49, 232, 211, 189, 64, 54, 108, 153, 132, 63,
			96, 103, 82, 186},
	}

	moduloValue = 0x12d

	log  []int
	alog []int
)

func init() {
	//Create log and antilog table
	log = make([]int, 256)
	alog = make([]int, 255)

	p := 1
	for i := 0; i < 255; i++ {
		alog[i] = p
		log[p] = i
		p *= 2
		if p >= 256 {
			p ^= moduloValue
		}
	}
}

// createECC200 Creates the ECC200 error correction for an encoded message.
//
// @param codewords  the codewords
// @param symbolInfo information about the symbol to be encoded
// @return the codewords with interleaved error correction.
//
func ErrorCorrection_EncodeECC200(codewords []byte, symbolInfo *SymbolInfo) ([]byte, error) {
	if len(codewords) != symbolInfo.GetDataCapacity() {
		return nil, gozxing.NewWriterException(
			"IllegalArgumentException: The number of codewords does not match the selected symbol")
	}
	sb := make([]byte, 0, symbolInfo.GetDataCapacity()+symbolInfo.GetErrorCodewords())
	sb = append(sb, codewords...)
	blockCount := symbolInfo.GetInterleavedBlockCount()
	if blockCount == 1 {
		ecc, e := createECCBlock(codewords, symbolInfo.GetErrorCodewords())
		if e != nil {
			return sb, e
		}
		sb = append(sb, ecc...)
	} else {
		sb = sb[:cap(sb)]
		dataSizes := make([]int, blockCount)
		errorSizes := make([]int, blockCount)
		for i := 0; i < blockCount; i++ {
			dataSizes[i] = symbolInfo.GetDataLengthForInterleavedBlock(i + 1)
			errorSizes[i] = symbolInfo.GetErrorLengthForInterleavedBlock(i + 1)
		}
		for block := 0; block < blockCount; block++ {
			temp := make([]byte, 0, dataSizes[block])
			for d := block; d < symbolInfo.GetDataCapacity(); d += blockCount {
				temp = append(temp, codewords[d])
			}
			ecc, _ := createECCBlock(temp, errorSizes[block])
			pos := 0
			for e := block; e < errorSizes[block]*blockCount; e += blockCount {
				sb[symbolInfo.GetDataCapacity()+e] = ecc[pos]
				pos++
			}
		}
	}
	return sb, nil
}

func createECCBlock(codewords []byte, numECWords int) ([]byte, error) {
	start := 0
	length := len(codewords)

	table := -1
	for i := 0; i < len(factorSets); i++ {
		if factorSets[i] == numECWords {
			table = i
			break
		}
	}
	if table < 0 {
		return codewords, gozxing.NewWriterException(
			"IllegalArgumentException: Illegal number of error correction codewords specified: %d",
			numECWords)
	}
	poly := factors[table]
	ecc := make([]int, numECWords)
	for i := start; i < start+length; i++ {
		m := ecc[numECWords-1] ^ int(codewords[i])
		for k := numECWords - 1; k > 0; k-- {
			if m != 0 && poly[k] != 0 {
				ecc[k] = ecc[k-1] ^ alog[(log[m]+log[poly[k]])%255]
			} else {
				ecc[k] = ecc[k-1]
			}
		}
		if m != 0 && poly[0] != 0 {
			ecc[0] = alog[(log[m]+log[poly[0]])%255]
		} else {
			ecc[0] = 0
		}
	}
	eccReversed := make([]byte, numECWords)
	for i := 0; i < numECWords; i++ {
		eccReversed[i] = byte(ecc[numECWords-i-1])
	}
	return eccReversed, nil
}
